﻿package game.alternativa.ModelViewer {
	import flash.utils.setTimeout;
	import flash.utils.clearTimeout;
	import flash.system.System;
	import game.errors.GameError;
	import game.objects.GameObject;
	import flash.display.Stage3D;
	import flash.display3D.Context3D;
	import flash.display3D.Context3DRenderMode;
	import flash.display3D.Context3DTriangleFace;
	import flash.events.Event;
	import alternativa.engine3d.core.Object3D;
	import game.alternativa.Model;
	import game.alternativa.ModelEvent;
	import game.objects.TextureObject;
	import flash.events.ErrorEvent;
	
	import alternativa.engine3d.primitives.Box;
	import alternativa.engine3d.primitives.GeoSphere;
	import alternativa.engine3d.core.Resource;
	import alternativa.engine3d.materials.EnvironmentMaterial;
	import alternativa.engine3d.resources.BitmapTextureResource;
	import flash.display.BitmapData;
	
	public class ModelViewer extends GameObject {
		
		private var scene:ModelScene;
		private var camera:ModelCamera;
		private var view:ModelView;
		private var models:Array = new Array;
		private var shadows:Array = new Array;
		private var params:IModelParams;
		private var container:Object3D;
		private var _width:Number = 200;
		private var _height:Number = 200;
		private var interval;
		private var _stopRender:Boolean = true;
		
		public function ModelViewer():void {
			
			this.addEventListener ( Event.ADDED_TO_STAGE, added );
			
		}
		
		private function added(event:Event):void {
			if (!params) {
				this.dispatchEvent( new ModelEvent ( ModelEvent.PARAMS_ERROR ) );
				return;
			}
			
			stage3D = stage.stage3Ds[0];
			
			stage.addEventListener(Event.ENTER_FRAME, enterFrame);
		}
		
		public function setupScene():void {
			scene = new ModelScene();
			
			container = new Object3D;
			
			view = new ModelView(_width, _height, 
							params.view.renderToBitmap, 
							params.view.backgroundColor, 
							params.view.backgroundAlpha, 
							params.view.antiAlias);
			addChild(view);
			
			camera = new ModelCamera(this, view, params);
			
			
			//addChild(camera.diagram);
			
			scene.addChild(camera);
			
			scene.addChild(container);
			
			/*
			var shadow:ModelShadow = new ModelShadow(params.shadow.width, params.shadow.height, params.shadow.nearBoundPosition, params.shadow.farBoundPosition, params.shadow.mapSize, params.shadow.pcfOffset);
			*/
			for (var str:String in objects) {
				var model:Model = objects[str];
				model.name = str;
				model.addEventListener ( ModelEvent.MODEL_COMPLETE,		modelEvent );
				model.addEventListener ( ModelEvent.MODEL_ERROR,		modelEvent );
				model.addEventListener ( ModelEvent.TEXTURE_COMPLETE,	modelEvent );
				container.addChild ( model );
			}
			
			
			
			/*var shadowLight:ModelShadowLight;
			for each (var iShadowLight:Object in params.shadowLights) {
				shadowLight = new ModelShadowLight;
				shadowLight.shadow = shadow;
				for (var iParamShadowLight:String in iShadowLight)
					shadowLight[iParamShadowLight] = iShadowLight[iParamShadowLight];
				container.addChild( shadowLight );
			}*/
			
			var light:ModelLight;
			for each (var iLight:Object in params.lights) {
				light = new ModelLight;
				for (var iParamLight:String in iLight) {
					light[iParamLight] = iLight[iParamLight];
				}
				container.addChild( light );
			}
			
			/*for each (var s:String in shadows) {
				if (objects[s])
					shadow.addCaster(objects[s]);
			}*/
		}
		
		public function a3d (object:String, value:String):void {
			if (models[object])
				(models[object] as Model).a3d = value;
		}
		
		
		public function textures (object:String, value:Vector.<TextureObject>):void {
			if (models[object])
				(models[object] as Model).textures = value;
		}
		
		
		private function contextCreationError(event:ErrorEvent):void {
			error(GameError.INIT_ERROR, 2);
		}
		
		private function onContext3DCreate(event:Event):void { 
			stage3D.removeEventListener(Event.CONTEXT3D_CREATE, onContext3DCreate);
			
			context3D = Stage3D( event.target ).context3D;
			context3D.enableErrorChecking = true;
			context3D.configureBackBuffer(_width, _height, (view) ? view.antiAlias : 8, true);
			
			//trace( "onContext3DCreate " + context3D.driverInfo );
			_stopRender = false;
			
			var newmodel:Array = new Array;
			for (var i:String in objects) {
				newmodel[i] = (objects[i] as Model).cloneModel();
			}
			
			models = newmodel;
			
			setupScene();
			
			for each (var model:Model in objects) {
				model.load()
			}
			
			this.dispatchEvent( new Event ( Event.COMPLETE ) );
			
			/*var mat:EnvironmentMaterial = new EnvironmentMaterial( new BitmapTextureResource( new BitmapData (1, 1, false, 0x000000) ), param.cubeGarage );
			
			
			var box:Box = new Box(256, 256, 256, 1, 1, 1, false, mat);
			box.z = 70;
			container.addChild(box);
			for each(var res:Resource in box.getResources()) {
				res.upload(stage3D.context3D);
			}*/
			
			
			/*var sphere:GeoSphere = new GeoSphere(256, 50, true, mat);
			sphere.z = 0;
			container.addChild(sphere);
			for each(var res:Resource in sphere.getResources()) {
				res.upload(context3D);
			}*/
			
			
		}
		
		private function enterFrame(e:Event):void {
			if (_stopRender || !view || !camera || !stage3D || !context3D) return;
			
			try {
				
				
				context3D.clear();
				camera.render ( stage3D );
				context3D.setRenderToBackBuffer();
				
			} catch (e:Error) {
				
			}
		}
		
		private function modelEvent(event:ModelEvent):void {
			this.dispatchEvent ( new ModelEvent ( event.type, event.data ) );
		}
		
		public function resize(_width:Number, _height:Number):void {
			_stopRender = true;
			
			try {
				view.visible = false;
				context3D.clear();
				while (this.numChildren)
					removeChildAt(0);
			} catch (e:Error) {
				
			}
			context3D = null;
			
			this._width = _width;
			this._height = _height;
			
			clearTimeout(interval);
			
			interval = setTimeout(update, config.updateDynamic*1000);
		}
		
		private function update():void {
			
			stage3D.addEventListener ( ErrorEvent.ERROR,		contextCreationError );
            stage3D.addEventListener ( Event.CONTEXT3D_CREATE,	onContext3DCreate );
			stage3D.requestContext3D ( Context3DRenderMode.AUTO );
			
		}
		
		public function get param ():IModelParams { return params; }
		
		public function set param (value:IModelParams):void {
			params = value;
			for each (var object:Object in params.models) {
				models[object.name] = new Model;
				if (object.a3d) {
					models[object.name].z = -5;
					(models[object.name] as Model).a3d = config.host + config.dirs.models + object.name + '/' + object.a3d;
					var _textures:Vector.<TextureObject> = object.textures;
					for each (var t:TextureObject in _textures)
						t.url = config.host + config.dirs.models + object.name + '/' + t.url;
					(models[object.name] as Model).textures = _textures;
				}
				if (object.shadow)
					shadows.push(object.name);
			}
		}
		
		public function get objects():Array { return models; }
		
		public function getModel(object:String):Model {
			return (objects[object] as Model);
		}
	}
	
}